<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>曲线绘制</title>
        <script type="text/javascript" src="dygraph.js"></script>
        <link rel="stylesheet" type="text/css" href="dygraph.css" />
        <style>
            #graphdiv2 {
                width: 1800px;
                height: 900px;
            }
            .line-selector {
                margin-top: 10px;
            }
            #lineSelector {
                width: 400px;
                height: 200px;
            }
            button {
                margin-left: 10px;
            }
        </style>
    </head>
    <body>
        <div id="graphdiv2"></div>
        <div class="line-selector">
            <label for="lineSelector">选择显示的线条：</label>
            <select id="lineSelector" multiple>
                <!-- 选项将通过JavaScript动态生成 -->
            </select>
            <button id="selectAllButton">全部</button>
            <div>
                <button id="loadData1">加载数据1</button>
                <button id="loadData2">加载数据2</button>
            </div>
        </div>
        <script type="text/javascript">
            let csvFilePath = 'output.csv'

            // 每条数据最大值存储数组
            // 标记，因为自定义的绘制时，会一条线一条线的调用customPlotter函数进行绘制，但是倍乘计算一次就可以（暂时不设置按钮进行倍乘控制），因此用来标记是否已经倍乘过了也可以
            const multiplication = []

            // 每条线的倍乘倍数存放数组
            var multiple = []

            // 添加按钮的事件监听器
            document
                .getElementById('loadData1')
                .addEventListener('click', function () {
                    csvFilePath = '测试数据.csv'
                    canvas(csvFilePath)
                })

            document
                .getElementById('loadData2')
                .addEventListener('click', function () {
                    csvFilePath = '倍乘.csv'
                    canvas(csvFilePath)
                })

            // 测试数据.csv中有50条线
            const lineCount = 50 // 线条数量
            const colors = [] // 存储线条的颜色和标签
            const visibility = new Array(lineCount).fill(false) // 初始所有线条不显示

            // 动态生成线条的颜色和标签
            for (let i = 0; i < lineCount; i++) {
                const hue = Math.floor(Math.random() * 360) // 随机生成颜色的色相
                const color = `hsl(${hue}, 70%, 50%)` // 使用HSL颜色模式生成颜色
                colors.push({
                    value: color,
                    label: `第${i + 1}条线`,
                    show: false, // 初始不显示
                })
            }

            // 寻找倍乘目标值，返回一个数组，存放每条线的倍乘倍数
            function drawPoint(allPoints, count) {
                // 寻找每条线中最大的值
                var max = 0
                for (var i = 0; i < count; i++) {
                    for (var j = 0; j < allPoints[i].length; j++) {
                        if (allPoints[i][j].yval > max) {
                            max = allPoints[i][j].yval
                        }
                    }
                    // 使用a数组暂时存储最大值
                    multiplication.push(max)
                    max = 0
                }

                // 计算每条线的最大值的平均值，用c存储
                for (var item of multiplication) {
                    // item指的的就是数组每一项的值
                    max += item
                }

                // 计算平均值
                const average = max / count

                console.log(average)

                // 在使用平均值除以最大值得到每条线的倍乘倍数作为返回值
                return multiplication.map(function (item, index) {
                    return average / item
                })
            }

            // plotter 自定义的数据处理绘制函数，是循环一条一条线的绘制，其中的e.seriesIndex是每条线的索引
            // 故如果要处理单条数据线的画，只需要知道其索引就可以进行相应的操作。
            function customPlotter(e) {
                var g = e.dygraph
                var series = e.seriesIndex
                var ctx = e.drawingContext
                var points = e.allSeriesPoints[series] // 获取全部系列的数据点

                // 计算倍乘数multiple
                // if(multiplication.length === 0 && series > 1)
                //     multiple = drawPoint(e.allSeriesPoints, e.seriesCount)
                //
                // for(var i = 0; i < points.length; i++)
                // {
                //     points[i].yval *= multiple[series]
                //     points[i].canvasy = g.toDomCoords(points[i].xval, points[i].yval)[1]
                // }

                ctx.beginPath()
                ctx.strokeStyle = e.color
                ctx.lineWidth = 1

                for (let i = 0; i < points.length - 1; i++) {
                    if (i === 0) {
                        ctx.moveTo(points[0].canvasx, points[0].canvasy)
                        continue
                    }
                    var p1 = points[i]
                    var p2 = points[i - 1]

                    if (p1.xval - p2.xval > 1000) {
                        // 如果 x 值的差大于 1000，则跳过绘制，这个地方用来判断横轴坐标是否连续，如果出现由10：00：00直接跳到11：00：00的话，直接跳过绘制
                        ctx.moveTo(p1.canvasx, p1.canvasy)
                        continue
                    }

                    // 判断当前时间点是否有数据，没有数据是''，会在其中显示为NaN，所以需要判断isNaN,如果是没有数据点，则跳过绘制
                    if (isNaN(p2.yval)) {
                        ctx.moveTo(p1.canvasx, p1.canvasy)
                        continue
                    }

                    ctx.lineTo(p1.canvasx, p1.canvasy)
                }

                ctx.stroke()
            }

            function canvas(csvFilePath) {
                multiplication.pop()
                // 初始化Dygraph
                Dygraph.onDOMready(function onDOMready() {
                    g2 = new Dygraph(
                        document.getElementById('graphdiv2'),
                        csvFilePath, // path to CSV file
                        {
                            title: '测试标题',
                            titleHeight: 100,
                            xlabel: '时间',
                            ylabel: '数值',
                            axisLabelWidth: 100,
                            drawPoints: true,
                            colors: colors.map((c) => c.value), // 动态设置颜色
                            visibility: visibility, // 初始所有线条不显示
                            panEdgeFraction: 0.01, // 限制平移范围
                            // fillGraph：填充折线下方（类似阴影颜色）如果需要增加阴影，会导致渲染速度的减缓，如果数据量很大，会导致数据渲染异常缓慢（大概有三、四分钟），甚至无法渲染至浏览器崩溃
                            // fillGraph: true,
                            labelsUTC: true, // 标签是否使用UTC时间，如果选择false会成为UTC+8
                            pixelsPerLabel: 100,
                            // 界面缩放到最小时只显示十秒的内容的回调函数（但目前会在缩放至最小时再次缩放会进行重绘，导致位置偏移，但仍是十秒）
                            zoomCallback: function (minDate, maxDate) {
                                // 限制最小缩放范围为10秒
                                const minZoomRange = 10 * 1000 // 10秒转换为毫秒
                                if (maxDate - minDate < minZoomRange) {
                                    g2.updateOptions({
                                        dateWindow: [
                                            minDate,
                                            minDate + minZoomRange,
                                        ],
                                    })
                                }
                            },
                            labelsDiv: document.getElementById('content'), // 控制线标签位置
                            // 横坐标显示正常日期的回调函数
                            // 因为现在csv文件中的时间列使用的是ISO 8601的格式时间，使用的是UTC时间，如果不作处理展示需要所有时间-8h
                            // 后续可以修改为UTC+8的时间
                            axes: {
                                x: {
                                    axisLabelFormatter: function (
                                        d,
                                        granularity,
                                        opts,
                                        dygraph
                                    ) {
                                        var date = new Date(d)
                                        const hours = String(
                                            date.getHours() >= 8
                                                ? date.getHours() - 8
                                                : date.getHours() + 24 - 8
                                        )
                                        const minute = String(date.getMinutes())
                                        const second = String(date.getSeconds())
                                        return (
                                            hours.padStart(2, '0') +
                                            ':' +
                                            minute.padStart(2, '0') +
                                            ':' +
                                            second.padStart(2, '0')
                                        )
                                    },
                                },
                            },
                            // 数据绘制处理回调函数,这里通过自定义的方式，进行曲线绘制，解决csv中的信息缺失，则在相应位置不进行绘制的问题，并且会增加渲染速度
                            plotter: [customPlotter],
                        }
                    )

                    // 动态生成选择框的选项
                    const selectElement =
                        document.getElementById('lineSelector')
                    colors.forEach((color, index) => {
                        const option = document.createElement('option')
                        option.value = index
                        option.text = color.label
                        selectElement.appendChild(option)
                    })

                    // 监听选择框的变化，更新线条的可见性
                    selectElement.addEventListener('change', function () {
                        const selectedLines = Array.from(
                            selectElement.selectedOptions
                        ).map((option) => parseInt(option.value))
                        const newVisibility = new Array(lineCount).fill(false) // 默认所有线条不显示
                        selectedLines.forEach((index) => {
                            newVisibility[index] = true // 将选中的线条设置为显示
                        })
                        g2.setVisibility(newVisibility) // 更新Dygraph的可见性
                    })

                    // 添加“全部”按钮的功能
                    const selectAllButton =
                        document.getElementById('selectAllButton')
                    let isAllSelected = false // 用于记录当前是否全选
                    selectAllButton.addEventListener('click', function () {
                        isAllSelected = !isAllSelected // 切换状态
                        const options = selectElement.options
                        const newVisibility = new Array(lineCount).fill(
                            isAllSelected
                        ) // 根据状态设置可见性
                        for (let i = 0; i < options.length; i++) {
                            options[i].selected = isAllSelected // 更新选项的选中状态
                        }
                        g2.setVisibility(newVisibility) // 更新Dygraph的可见性
                    })
                })
            }

            canvas(csvFilePath)
        </script>
    </body>
</html>
